var tgd = tgd || {};
tgd.Utilities = tgd.Utilities || {};

tgd.Utilities.StringBuilder = function () {
	this._data = [];
	this._length = 0;

	if (tgd.Utilities.StringBuilder._initialized === undefined) {

		tgd.Utilities.StringBuilder.prototype.append = function(str) {
			this._data[this._length++] = str;
			return this;
		}

		tgd.Utilities.StringBuilder.prototype.repeat = function(str, cnt) {
			for (var i = 0; i < cnt; ++i, this._data[this._length++] = str);
			return this;
		}

		tgd.Utilities.StringBuilder.prototype.toString = function() {
			return this._data.join("");
		}

		tgd.Utilities.StringBuilder._initialized = true;
	}
}




var tgd = tgd || {};
tgd.Utilities = tgd.Utilities || {};
tgd.Utilities.Calendar = tgd.Utilities.Calendar || {};

tgd.Utilities.Calendar.DayNames = {
	0 : "Sunday",
	1 : "Monday",
	2 : "Tuesday",
	3 : "Wednesday",
	4 : "Thursday",
	5 : "Friday",
	6 : "Saturday",
	"Sunday" : 0,
	"Monday" : 1,
	"Tuesday": 2,
	"Wednesday": 3,
	"Thursday": 4,
	"Friday": 5,
	"Saturday": 6,
	getDayName : function(idx) { return this[idx]; },
	getDayAbbreviation : function(idx) { return this[idx].substr(0, 1); }
}



var tgd = tgd || {};
tgd.Utilities = tgd.Utilities || {};
tgd.Utilities.Calendar = tgd.Utilities.Calendar || {};

tgd.Utilities.Calendar.MonthNames = {
	0 : "Jan",
	1 : "Feb",
	2 : "Mar",
	3 : "Apr",
	4 : "May",
	5 : "Jun",
	6 : "Jul",
	7 : "Aug",
	8 : "Sep",
	9 : "Oct",
	10 : "Nov",
	11 : "Dec",
	"Jan" : 0,
	"Feb" : 1,
	"Mar": 2,
	"Apr": 3,
	"May": 4,
	"Jun": 5,
	"Jul": 6,
	"Aug" : 7,
	"Sep" : 8,
	"Oct" : 9,
	"Nov" : 10,
	"Dec" : 11,
	getMonthName : function(idx) { return this[idx]; }
}




var tgd = tgd || {};
tgd.Utilities = tgd.Utilities || {};
tgd.Utilities.Calendar = tgd.Utilities.Calendar || {};

tgd.Utilities.Calendar.Month = function (year, month) {
	var MS_PER_DAY = 86400000;  //milliseconds per day

	this._year = year;
	this._month = month;

	this._firstDayOfMonth = new Date(this._year, this._month, 1);
	this._firstDayOfWeek = this._firstDayOfMonth.getDay();
	this._lastDayOfMonth = new Date(new Date(this._year, this._month + 1, 1).getTime() - MS_PER_DAY);

	if (tgd.Utilities.Calendar.Month._initialized === undefined) {

		tgd.Utilities.Calendar.Month.prototype.getMonthName = function() {
			return tgd.Utilities.Calendar.MonthNames.getMonthName(this._month);
		}

		tgd.Utilities.Calendar.Month.prototype.getYear = function() {
			return this._year;
		}

		tgd.Utilities.Calendar.Month.prototype.getMonth = function() {
			return this._month;
		}

		tgd.Utilities.Calendar.Month.prototype.getFirstDayOfWeek = function() {
			return this._firstDayOfWeek;
		}

		tgd.Utilities.Calendar.Month.prototype.getNumberOfDaysInMonth = function() {
			return this._lastDayOfMonth.getDate();
		}

		tgd.Utilities.Calendar.Month._initialized = true;
	}
}





var tgd = tgd || {};
tgd.Utilities = tgd.Utilities || {};
tgd.Utilities.Calendar = tgd.Utilities.Calendar || {};
tgd.Utilities.Calendar.Views = tgd.Utilities.Calendar.Views || {};

tgd.Utilities.Calendar.Views.HTMLMonth = function (canvas, month) {
	this._canvas = canvas;
	this._month = month;
	this._tbl = null;

	var row = null;
	var cell = null;

	if (tgd.Utilities.Calendar.Views.HTMLMonth._initialized === undefined) {

		tgd.Utilities.Calendar.Views.HTMLMonth.prototype._buildTable = function() {
			this._tbl = document.createElement("table");
			/* MONTH AND YEAR HEADER */
			row = this._tbl.insertRow(0);
			/* MONTH CELL */
			var cell = row.insertCell(0);
			cell.colSpan = 4;
			cell.style.textAlign = "left";
			/* YEAR CELL */
			cell = row.insertCell(1);
			cell.colSpan = 3;
			cell.style.textAlign = "right";

			/* WEEK DAYS ABBREVIATIONS */
			row = this._tbl.insertRow(1);
			row.className = "weekdays";
			for (var i = 0; i < 7; ++i) {
				cell = row.insertCell(i);
				cell.innerHTML = tgd.Utilities.Calendar.DayNames.getDayAbbreviation(i);
			}

			/* DAYS OF THE MONTH 5 ROWS 7 COLUMNS */
			for (var i = 0; i < 6; ++i) {  //5 rows in the calendar
				row = this._tbl.insertRow(i + 2);
				for (var j = 0; j < 7; ++j) {  //7 days per week
					cell = row.insertCell(j);
					cell.innerHTML = "&nbsp;";
				}
			}
		}

		tgd.Utilities.Calendar.Views.HTMLMonth.prototype._setTableValues = function() {
			var isCurrentMonth = (this._month.getYear() == new Date().getFullYear() && this._month.getMonth() == new Date().getMonth());
			var lastMonth = new tgd.Utilities.Calendar.Month(this._month.getYear() - ((this._month.getMonth() + 11) % 12 == 11 ? 1 : 0), (this._month.getMonth() + 11) % 12);

			/* YEAR AND MONTH HEADING */
			this._tbl.rows[0].cells[0].innerHTML = this._month.getMonthName();
			this._tbl.rows[0].cells[1].innerHTML = this._month.getYear();

			/* DAYS OF THE MONTH */
			var cur = 0;
			for (var i = 0; i < 6; ++i) {  //5 rows in the calendar
				row = this._tbl.rows[i + 2];
				for (var j = 0; j < 7; ++j) {  //7 days per week
					cell = row.cells[j];
					if (cur === 0 && j === this._month.getFirstDayOfWeek()) {
						++cur;
					}
						
					if (cur === 0) {
						cell.innerHTML = lastMonth.getNumberOfDaysInMonth() - this._month.getFirstDayOfWeek() + j + 1;
						cell.className = "previousmonth";
					} else if (cur > this._month.getNumberOfDaysInMonth()) {
						cell.innerHTML = cur - this._month.getNumberOfDaysInMonth();
						cell.className = "nextmonth";
						++cur;
					} else {
						cell.className = (isCurrentMonth && cur == new Date().getDate() ? "today" : "");
						cell.innerHTML = cur
						++cur;
					}
				}
			}
		}

		tgd.Utilities.Calendar.Views.HTMLMonth.prototype.setMonth = function(month) {
			this._month = month;
			this._setTableValues();
		}

		tgd.Utilities.Calendar.Views.HTMLMonth.prototype.hideMonthYear = function() {
			this._tbl.rows[0].style.display = "none";
		}

		tgd.Utilities.Calendar.Views.HTMLMonth._initialized = true;
	}

	this._buildTable();
	this._setTableValues();

	/* ADD TABLE TO CANVAS */
	this._canvas.appendChild(this._tbl);
}





var tgd = tgd || {};
tgd.Utilities = tgd.Utilities || {};
tgd.Utilities.Calendar = tgd.Utilities.Calendar || {};
tgd.Utilities.Calendar.Views = tgd.Utilities.Calendar.Views || {};

tgd.Utilities.Calendar.Views.MonthPicker = function (canvas, year, month) {
	this._canvas = canvas;
	this._year = year;
	this._month = month;
	this.onMonthChanged = null;
	this._btnPrev = document.createElement("span");
	this._btnNext = document.createElement("span");
	this._monthDisplay = document.createElement("select");
	this._yearDisplay = document.createElement("span");

	var self = this;

	this._yearDisplay.className = "year";
	this._monthDisplay.className = "month";

	for (var i = 0; i < 12; ++i) {
		var opt = document.createElement("option");
		opt.text = tgd.Utilities.Calendar.MonthNames[i];
		opt.value = i;
		this._monthDisplay.options[i] = opt;
	}

	this._yearDisplay.innerHTML = this._year;
	this._monthDisplay.options[this._month].selected = true;

	this._monthDisplay.onchange = function() {
		self._month = self._monthDisplay.selectedIndex;

		if (self.onMonthChanged) {
			self.onMonthChanged();
		}
	}

	/* SETUP PREVIOUS MONTH BUTTON */
	this._btnPrev.innerHTML = "&laquo;";
	this._btnPrev.className = "button previous";
	this._btnPrev.onclick = function() {
		self._monthDisplay.options[self._month = ((self._month + 11) % 12)].selected = true;

		if (self._month == 11) {  //previous year
			self._yearDisplay.innerHTML = --self._year;
		}

		if (self.onMonthChanged) {
			self.onMonthChanged();
		}
	};

	/* SETUP NEXT MONTH BUTTON */
	this._btnNext.innerHTML = "&raquo;";
	this._btnNext.className = "button next";
	this._btnNext.onclick = function() {
		self._monthDisplay.options[self._month = (++self._month % 12)].selected = true;

		if (self._month == 0) {  //next year
			self._yearDisplay.innerHTML = ++self._year;
		}

		if (self.onMonthChanged) {
			self.onMonthChanged();
		}
	};

	/* ADD DOM ELEMENTS TO CANVAS */
	this._canvas.appendChild(this._btnPrev);
	this._canvas.appendChild(this._monthDisplay);
	this._canvas.appendChild(this._yearDisplay);
	this._canvas.appendChild(this._monthDisplay);
	this._canvas.appendChild(this._btnNext);

	if (tgd.Utilities.Calendar.Views.MonthPicker._initialized === undefined) {

		tgd.Utilities.Calendar.Views.MonthPicker.prototype.getYear = function() {
			return this._year;
		}

		tgd.Utilities.Calendar.Views.MonthPicker.prototype.getMonth = function() {
			return this._month;
		}

		tgd.Utilities.Calendar.Views.MonthPicker.prototype.setMonth = function(y, m) {
			this._year = y;
			this._month = m;
			this._yearDisplay.innerHTML = this._year;
			this._monthDisplay.options[this._month].selected = true;
		}		

		tgd.Utilities.Calendar.Views.MonthPicker._initialized = true;
	}
}





var tgd = tgd || {};
tgd.Utilities = tgd.Utilities || {};
tgd.Utilities.Calendar = tgd.Utilities.Calendar || {};
tgd.Utilities.Calendar.Views = tgd.Utilities.Calendar.Views || {};

tgd.Utilities.Calendar.Views.TodayPicker = function (canvas) {
	this._canvas = canvas;
	this.onTodayPicked = null;
	this._today = new Date();
	this._todayLink = document.createElement("span");

	var self = this;

	this._todayLink.className = "link";
	this._todayLink.innerHTML = "Today";
	this._todayLink.onclick = function() { self.onTodayPicked(); };

	this._canvas.appendChild(this._todayLink);
	this._canvas.appendChild(document.createTextNode(" is " + tgd.Utilities.Calendar.MonthNames[this._today.getMonth()] + " " + this._today.getDate() + ", " + this._today.getFullYear()));

	if (tgd.Utilities.Calendar.Views.TodayPicker._initialized === undefined) {

		tgd.Utilities.Calendar.Views.TodayPicker.prototype.getMonth = function() {
			return this._today.getMonth();
		}

		tgd.Utilities.Calendar.Views.TodayPicker.prototype.getYear = function() {
			return this._today.getFullYear();
		}

		tgd.Utilities.Calendar.Views.TodayPicker._initialized = true;
	}
}


			function init() {
				var calDiv = document.getElementById("cal");
				var monthDiv = document.getElementById("month");
				var todayDiv = document.getElementById("today");
				var monthCal;
				var monthPicker;
				var todayPicker;
				var calView;

				/* SETUP CALENDAR MODEL */
				monthCal = new tgd.Utilities.Calendar.Month(new Date().getFullYear(), new Date().getMonth());

				/* SETUP CALENDAR VIEW */
				calView = new tgd.Utilities.Calendar.Views.HTMLMonth(calDiv, monthCal);
				calView.hideMonthYear();

				/* SETUP TODAY PICKER */
				todayPicker = new tgd.Utilities.Calendar.Views.TodayPicker(todayDiv);
				todayPicker.onTodayPicked = function() {
					monthCal = new tgd.Utilities.Calendar.Month(todayPicker.getYear(), todayPicker.getMonth());
					monthPicker.setMonth(todayPicker.getYear(), todayPicker.getMonth());
					calView.setMonth(monthCal);
				}

				/* SETUP MONTH PICKER */
				monthPicker = new tgd.Utilities.Calendar.Views.MonthPicker(monthDiv, new Date().getFullYear(), new Date().getMonth());
				monthPicker.onMonthChanged = function() {
					monthCal = new tgd.Utilities.Calendar.Month(monthPicker.getYear(), monthPicker.getMonth());
					calView.setMonth(monthCal);
				}
			}